# -*- coding:utf8 -*-

import math
from PyQt4.QtCore import QPoint, QPointF

p0 = QPoint()
p1 = QPoint()
p2 = QPoint()
p3 = QPoint()

ITERATION_SUM = 100
ACCURACY = 0.000001


class Parameterize(object):
    """
    弧长参数化实现类
    提供下面几个函数：
        1. set_length(points_totalnum)
        2. get_point_by_arclength(arclength)
        3. get_arclength(start_u, end_u)
        4. get_coefficient(arc_num)
    """

    def __init__(self, spline):
        """

        :param spline: Spline, spline对象
        """
        self.spline = spline
        self.coefficient_a = float()
        self.coefficient_b = float()
        self.coefficient_c = float()
        self.coefficient_d = float()
        self.coefficient_e = float()
        self.point_list = list()    # p0,p1,p2,p3
        self.seg_length_list = [0.0]    # 第一段为0
        self.total_arclength = 0.0  # 曲线总长度

    def init_data(self, points_num):
        """初始化类之后，首先调用此函数初始化数据
        计算 积分参数
        """
        seg_num = points_num - 3
        arclength = 0

        """得到每一段弧长的长度"""
        for i in range(0, seg_num):
            self.get_coefficient(i)
            arclength += self.get_arclength(0.0, 1.0)
            self.seg_length_list.append(arclength)
        self.total_arclength = arclength

        """归一化到(0,1)"""
        for i in range(0, seg_num+1):
            self.seg_length_list[i] /= self.total_arclength

    def get_coefficient(self, arc_num):
        """计算系数

        :param arc_num: int, 弧长编号
        """
        matrix = self.spline.m()
        points = self.spline.spline()
        # for i in range(0, 4):
            # pt = QPoint()
            # pt.setX(
            #     m[i][0]*points[arc_num].x() + m[i][1]*points[arc_num+1].x() +
            #     m[i][2]*points[arc_num+2].x() + m[i][3]*points[arc_num+3].x()
            # )
            # pt.setY(
            #     m[i][0]*points[arc_num].y() + m[i][1]*points[arc_num+1].y() +
            #     m[i][2]*points[arc_num+2].y() + m[i][3]*points[arc_num+3].y()
            # )
            # self.point_list.append(pt)
        m = []
        for i in range(0, len(matrix)):
            for j in range(0, len(matrix[0])):
                m.append(matrix[i][j])
        o = arc_num
        p0.setX(m[0]*points[o].x()+m[1]*points[o+1].x()+m[2]*points[o+2].x()+m[3]*points[o+3].x());
        p1.setX(m[4]*points[o].x()+m[5]*points[o+1].x()+m[6]*points[o+2].x()+m[7]*points[o+3].x());
        p2.setX(m[8]*points[o].x()+m[9]*points[o+1].x()+m[10]*points[o+2].x()+m[11]*points[o+3].x());
        p3.setX(m[12]*points[o].x()+m[13]*points[o+1].x()+m[14]*points[o+2].x()+m[15]*points[o+3].x());

        p0.setY(m[0]*points[o].y()+m[1]*points[o+1].y()+m[2]*points[o+2].y()+m[3]*points[o+3].y());
        p1.setY(m[4]*points[o].y()+m[5]*points[o+1].y()+m[6]*points[o+2].y()+m[7]*points[o+3].y());
        p2.setY(m[8]*points[o].y()+m[9]*points[o+1].y()+m[10]*points[o+2].y()+m[11]*points[o+3].y());
        p3.setY(m[12]*points[o].y()+m[13]*points[o+1].y()+m[14]*points[o+2].y()+m[15]*points[o+3].y());
        # p0 = self.point_list[0]
        # p1 = self.point_list[1]
        # p2 = self.point_list[2]

        self.coefficient_a = 9*(p0.x()*p0.x() + p0.y()*p0.y())
        self.coefficient_b = 12*(p0.x()*p1.x() + p0.y()*p1.y())
        self.coefficient_c = 6*(p0.x()*p2.x() + p0.y()*p2.y()) \
            + 4*(p1.x()*p1.x()+p1.y()*p1.y())
        self.coefficient_d = 4*(p1.x()*p2.x() + p1.y()*p2.y())
        self.coefficient_e = p2.x()*p2.x() + p2.y()*p2.y()

    def get_point_by_arclength(self, seg_length):
        """根据弧长返回一个点
        :param seg_length:
        """
        u_l = 0.0
        u_r = 1.0
        seg_num = 0

        """判断属于哪一段,得到seg_num"""
        # for index,length in enumerate(self.seg_length_list):
        #     if length >= seg_length:
        #         seg_num = index

        while True:
            seg_num += 1
            if (self.seg_length_list[seg_num] >= seg_length):
                seg_num -= 1
                break;

        """得到该段弧的真实长度"""
        seg_dis = self.total_arclength * (seg_length - \
            self.seg_length_list[seg_num])

        """计算系数"""
        self.get_coefficient(seg_num)
        """二分法得到u"""
        u = 0.0
        count = 0
        while True:
            count += 1
            u = (u_l+u_r)*1.0 / 2
            seg_arclength = self.get_arclength(u_l, u)
            if(seg_dis < seg_arclength + ACCURACY):
                u_r = u
            else:
                u_l = u
                seg_dis -= seg_arclength
            if (abs(seg_dis - seg_arclength) < ACCURACY):
                    # (seg_dis > seg_arclength - ACCURACY):
                break
        """从u值计算起始点"""
        pt = QPointF()
        pt.setX(p0.x()*u**3 +
                p1.x()*u**2 +
                p2.x()*u +
                p3.x())
        pt.setY(p0.y()*u**3 +
                p1.y()*u**2 +
                p2.y()*u +
                p3.y())
        return pt

    def get_arclength(self, u_l, u_r):
        """扩展simpson方法求弧长"""
        deta = (u_r-u_l) / ITERATION_SUM
        u = u_l + deta
        total = 0.0

        for i in range(2, ITERATION_SUM):
            if not (i&1):
                total += 4.0*self.integrand(u)
            else:
                total += 2.0*self.integrand(u)
            u += deta
        arclength = deta * (self.integrand(u_l) + total +
            self.integrand(u_r)) / 3.0
        return arclength

    def integrand(self, u):
        """求得积分"""
        integration_result = 0.0
        integration_result = (
            self.coefficient_a*(u**4) +
            self.coefficient_b*(u**3) +
            self.coefficient_c*(u**2) +
            self.coefficient_d*u +
            self.coefficient_e
        )
        integration_result = math.sqrt(integration_result)
        return  integration_result
